package com.rivues.task.resource;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Restrictions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.rivues.core.RivuDataContext;
import com.rivues.module.platform.web.bean.TaskInfo;
import com.rivues.module.platform.web.model.AnalyzerReport;
import com.rivues.module.platform.web.model.AnalyzerReportModel;
import com.rivues.module.platform.web.model.Cube;
import com.rivues.module.platform.web.model.JobDetail;
import com.rivues.module.platform.web.model.QueryLog;
import com.rivues.module.report.web.model.PublishedReport;
import com.rivues.util.RivuTools;
import com.rivues.util.iface.report.R3Request;
import com.rivues.util.serialize.JSON;
import com.rivues.util.service.ServiceHelper;

import freemarker.template.TemplateException;

public class LocalShellResource extends Resource {
	private final Logger log = LoggerFactory.getLogger(LocalShellResource.class); 
	private JobDetail job ;
	private List<String> shellList = new ArrayList<String>() ;
	private List<File> inputFileList = new ArrayList<File>();
	
	/**
	 * 构造器
	 * @param job
	 */
	public LocalShellResource(JobDetail job){
		this.job = job ;
		
		/**
		 * 如果在任务中输入了  文件目录，则将文件目录放入到  脚本的参数中，输入的文件参数可以使文件的绝对路径，也可以使目录的绝对路径，如果是目录，则会
		 * 遍历目录下的所有文件，例如：
		 * *   /usr/local/data/20140628 , 那么用户可以再  “本地执行” 任务目录里可以填入 /usr/local/${.now?string('yyyyMMdd')}，即可能够在每天执行一次的任务
		 * 中自动生成目录的名称，同时会将目录下的所有文件遍历出来，加入到脚本的参数列表中，在写脚本的时候同样支持  Freemarker脚本
		 * 
		 * 例如 ：<#if file??>/hadoop-exp/hadoop-exp.sh -i ${file.absolutePath} -X -H IB-1 -Lroot -P5029 -Dnmsop -T ${file.name} --fields-terminated-by=","</#if> 
		 * 其中，file是放入Freemarker模板的固定参数
 		 */
		if(this.job.getArearule()!=null){
			try {
				initInputFileList(inputFileList , new File(RivuTools.getTemplet(this.job.getArearule(), new HashMap<String, Object>())));
			} catch (IOException e) {
				e.printStackTrace();
			} catch (TemplateException e) {
				e.printStackTrace();
			}
		}
		for(File file : inputFileList){
			Map<String , Object> values = new HashMap<String, Object>();
			values.put("file", file) ;
			try {
				String taskInfo = RivuTools.getTemplet(this.job.getTaskinfo(), values) ;
				if(taskInfo!=null){
					for(String shell : taskInfo.split("\n")){
						if(shell.trim().length()>0){
							shellList.add(shell) ;
						}
					}
				}
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (TemplateException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
	/**
	 * 
	 * @param fileList
	 * @param root
	 */
	private void initInputFileList(List<File> fileList , File root){
		if(root.exists()){
			if(root.isDirectory()){
				File[] listFiles = root.listFiles() ;
				for(File file : listFiles){
					initInputFileList(fileList , file) ;
				}
			}else{
				inputFileList.add(root) ;
			}
		}
	}
	@Override
	public void begin() throws Exception {
		
	}

	@Override
	public void end(boolean clear) throws Exception {
		this.job.getReport().setPages(0) ;
	}

	@Override
	public JobDetail getJob() {
		return job;
	}

	@Override
	public void process(OutputTextFormat meta, JobDetail job) {
		Process process = null ;
		/**
		 * 执行本地Shell
		 */
		try {
			process = Runtime.getRuntime().exec(meta.getTitle()) ;
			/**
			 * 避免出现线程阻塞，同时启用两个输出处理线程，标准的IO（ERR OUT , STD OUT）
			 */
			Thread errThread = new ProcessOutThread(process.getInputStream() , "error");
			Thread outThread = new ProcessOutThread(process.getErrorStream() , "out");
			errThread.start() ;
			outThread.start();
			process.waitFor();
			/**
			 * 等待任务执行完成
			 */
			while(!job.getReport().isError() || !job.getReport().isOut()){
				Thread.sleep(100) ;
			}
			 
		} catch (Exception e) {
			this.job.getReport().setDetailmsg(e.getMessage()) ;
			e.printStackTrace();
		}
	}
	
	private void processOutput(InputStream input){
		
	}
	
	@Override
	public OutputTextFormat next() throws Exception {
		OutputTextFormat outTextFormat = null ;
		if(this.shellList.size()>0 && this.shellList.get(0)!=null && this.shellList.get(0).length()>0){
			outTextFormat = new OutputTextFormat(job) ;
			outTextFormat.setTitle(this.shellList.remove(0)) ;
		}
		return outTextFormat;
	}

	@Override
	public boolean isAvailable() {
		// TODO Auto-generated method stub
		return true;
	}

	@Override
	public OutputTextFormat getText(OutputTextFormat object) throws Exception {
		return object;
	}

	@Override
	public void rmResource() {
		
	}

	@Override
	public void updateTask() {
		
	}
	
	class ProcessOutThread extends Thread{
		private InputStream input ;
		private String type ;
		ProcessOutThread(InputStream input , String type){
			this.input = input ;
			this.type = type ;
		}
		public void run(){
			BufferedReader br = new BufferedReader(new InputStreamReader(input)); 
			StringBuffer strb = new StringBuffer() ;
			String line = null ;
			
			try {
				while((line = br.readLine())!=null){
					strb.append(line).append("<br/>") ;
					/**
					 * 根据souce 和target 判断开始和结束任务 标记，一旦遇到，则  终止执行 线程。
					 */
					if((job.getSource()!=null && job.getSource().length()>0  && strb.indexOf(job.getSource())>=0) || (job.getTargettask()!=null && job.getTargettask().length()>0 && strb.indexOf(job.getTargettask())>=0)){
						break ;
					}
					/**
					 * 验证超长，避免占用大内存
					 */
					if(strb.toString().length()>255){
						strb = new StringBuffer(strb.substring(strb.length()-255, strb.length()));
					}
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
			if(strb.length() > 255){
				job.getReport().setDetailmsg(strb.substring(strb.length()-255, strb.length())) ;
			}else{
				job.getReport().setDetailmsg(strb.toString()) ;
			}
			if(type!=null && type.equals("error")){
				job.getReport().setError(true) ;
			}
			if(type!=null && type.equals("out")){
				job.getReport().setOut(true) ;
			}
		}
	}

}
